import random

import elasticsearch
from django.db.models import Q

from rest_framework.decorators import action
from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin
from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination
from rest_framework.response import Response
from rest_framework.status import HTTP_404_NOT_FOUND
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from rest_framework.permissions import IsAuthenticated

from .models import Label, Topic
from .serializers import LabelSerializer, TopicSerializer, TopicNoAnswerSerializer, \
    LabelTopicNoAnswerSerializer
from users.utils import wrap_permission, Mypermission
from elasticsearch import Elasticsearch
from config.elasticsearchConfig import HOST, PORT

# Create your views here.

# 实例化创建es连接对象 ES是一个集群，所以可以传多个ip和端口，因此第一个参数时字典
es = Elasticsearch([{'host': HOST, 'port': PORT}], timeout=3600)


class LabelViewSet(GenericViewSet, ListModelMixin, CreateModelMixin, UpdateModelMixin):
    permission_classes = [IsAuthenticated]  # 类视图的全局权限
    queryset = Label.objects.all()
    serializer_class = LabelSerializer

    @wrap_permission(Mypermission)
    def create(self, request, *args, **kwargs):  # 创建标签时user字段自动传入登录的用户id
        request.POST._mutable = True  # 使querydict可修改
        request.data['user'] = str(request.user.id)
        return CreateModelMixin.create(self, request, *args, **kwargs)

    def update(self, request, *args, **kwargs):  # 只有创建该标签的用户才有修改权限
        if not Label.objects.filter(id=kwargs['pk'], user=request.user):
            return Response({'detail': '您没有修改权限'})
        request.POST._mutable = True  # 使querydict可修改
        request.data['user'] = str(request.user.id)

        return UpdateModelMixin.update(self, request, *args, **kwargs)

    @action(methods=['get'], detail=True)
    def examination(self, request, pk):  # 阶段测试接口 返回指定标签的题目
        # 查询标签
        try:
            label = Label.objects.get(id=pk)
        except Label.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)
        serializer = LabelTopicNoAnswerSerializer(label)
        a = 0
        # 被逻辑删除的题目不返回
        for i in serializer.data['topic_set']:  # OrderDict通过.get取值
            detail = Topic.objects.filter(id=i.get('id'), is_delete=False)  # 判断题目有没有被逻辑删除
            if not detail:
                serializer.data['topic_set'].pop(a)
            a = a + 1

        return Response(serializer.data)


class TopicPaginationPageNumber(PageNumberPagination):
    page_size = 5
    page_size_query_param = 'size'  # 每页显示条数
    page_query_param = 'page'  # 默认就是page,多少页
    max_page_size = 100


class TopicViewSet(ModelViewSet):  # 这里是老师身份的题目操作
    permission_classes = [Mypermission]
    queryset = Topic.objects.filter(is_delete=False)
    serializer_class = TopicSerializer

    pagination_class = TopicPaginationPageNumber

    # 需要一个随机抽取n道题 增加 修改 删除 查多条
    def create(self, request, *args, **kwargs):  # 创建题目时user字段自动传入登录的用户id
        request.POST._mutable = True  # 使querydict可修改
        request.data['user'] = str(request.user.id)

        # 反序列化 校验 通过则持久化到数据库
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()

        # 拿到添加后的序列化数据 往ES的topic索引中添加文档数据
        res_data = serializer.data
        es.index(index='topic', id=res_data['id'], body=res_data)

        return Response(res_data)

    def update(self, request, *args, **kwargs):  # 只有创建该题目的用户才有修改权限
        if not Topic.objects.filter(id=kwargs['pk'], user=request.user):
            return Response({'detail': '您没有修改权限'})

        request.POST._mutable = True  # 使querydict可修改
        request.data['user'] = str(request.user.id)

        try:
            topic = Topic.objects.get(id=kwargs['pk'])
        except Topic.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)
        serializer = self.get_serializer(instance=topic, data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()

        res_data = serializer.data
        # 这里es的update是post的修改方式
        es.update(index='topic', id=res_data['id'], body={"doc": res_data})

        return Response(res_data)

    def destroy(self, request, *args, **kwargs):
        if not Topic.objects.filter(id=kwargs['pk'], user=request.user):
            return Response({'detail': '您没有删除权限'})
        res_data = ModelViewSet.destroy(self, request, *args, **kwargs)
        es.delete(index='topic', id=kwargs['pk'])
        return res_data

    # @action(methods=['get'], detail=False)
    # def inites(self, request):
    #     serializer = self.get_serializer(self.get_queryset(), many=True)
    #     data = serializer.data
    #     for i in data:
    #         es.index(index='topic', id=i['id'], body=i)
    #
    #     return Response(serializer.data)

    @action(methods=['get'], detail=False)
    def search(self, request):  # 这个视图要模拟work/topic
        # 地址栏参数获取与初始化
        # 拿到get请求的参数，也就是地址栏的参数
        data = dict(request.query_params)
        page = int(data.get('page')[0]) if data.get('page') else 1
        size = int(data.get('size')[0]) if data.get('size') else 5
        subject = data.get('subject')[0] if data.get('subject') else ''
        # print(request.query_params)
        # print(request.data)

        # es搜索
        query = {
            "query": {

            },
            "highlight": {
                "pre_tags": "<strong style='color:red'>",
                "post_tags": "</strong>",
                "fields": {
                    "subject": {}
                }
            },
            "from": (page - 1) * size,
            "size": size
        }
        if not subject:
            query['query'] = {"match_all": {}}
        else:
            query['query'] = {"match": {
                "subject": subject  # 题目
            }}

        try:
            print(query)
            es_res = es.search(index='topic', body=query)  # body里面传query

            def highlight(i):
                if i.get('highlight'):
                    i['_source']['subject'] = i['highlight']['subject'][0]
                return i['_source']

            count = es_res["hits"]["total"]["value"]
            subject_url = f'subject={subject}' if subject else ''
            next = f'http://127.0.0.1:8000/work/topic/search/?{subject_url}&page={page + 1}&size={size}' if page * size < count else None
            previous = f'http://127.0.0.1:8000/work/topic/search/?{subject_url}&page={page - 1}&size={size}' if page > 1 else None

            results = [highlight(i) for i in es_res["hits"]["hits"]]
            res = {
                "count": es_res["hits"]["total"]["value"],
                "next": next,
                "previous": previous,
                "results": results
            }
        except elasticsearch.exceptions.NotFoundError:
            res = {
                "count": 0,
                "next": None,
                "previous": None,
                "results": []
            }
        return Response(res)


# 如果要指定开始查询的条数就用这种分页方式
class TopicPagination(LimitOffsetPagination):  # 实现懒加载 本质是分页
    default_limit = 10  # 默认每页5条 最多5条，少了不会报错
    # limit_query_param = 'limit' # 指定查询条数的参数 ?limit=20
    # offset_query_param = 'offset'  开始查询从0开始算
    max_limit = 20  # ?limit=10&offset=5  从第五条开始查询后面的10条数据


class TopicStudentViewSet(GenericViewSet, ListModelMixin):  # 学生身份的题目操作s
    permission_classes = [IsAuthenticated]
    # Q查询， ~取反  &与   |或
    # 查询标签名不包含试卷的题目 让学生看不到试卷题目
    # is_delete=False or label != 试卷
    queryset = Topic.objects.filter(
        Q(is_delete=False) & ~Q(label__name__contains='试卷') & ~Q(label__name__contains='试题'))
    serializer_class = TopicSerializer

    pagination_class = TopicPagination  # 设置分页

    # 实现随机抽题功能
    @action(methods=['get'],detail=False)
    def random(self, request):  # /work/topics/random/
        data = dict(request.query_params)
        size = data.get('size')[0]
        size = int(size) if size else 3
        # 先抽取再去掉答案，节约资源
        serializer = TopicNoAnswerSerializer(self.get_queryset(), many=True)
        # data = random.sample(self.get_serializer(self.get_queryset(), many=True).data, size)
        # for i in data:
        #     i.pop('answer')
        # return Response(random.sample(serializer.data, size))
        return Response(random.sample(serializer.data, size))

    @action(methods=['get'], detail=False)
    def search(self, request):  # 这个视图要模拟work/topic
        # 地址栏参数获取与初始化
        # 拿到get请求的参数，也就是地址栏的参数
        data = dict(request.query_params)
        offset = int(data.get('offset')[0]) if data.get('offset') else 0
        limit = int(data.get('limit')[0]) if data.get('limit') else 10
        subject = data.get('subject')[0] if data.get('subject') else ''
        # print(request.query_params)
        # print(request.data)

        # es搜索
        query = {
            "query": {
                "bool": {
                    # should和must_not之间的联系是and
                    "should": [

                    ],
                    "must_not": [
                        {
                            "match": {
                                "label_name": "试卷试题"
                            }
                        }
                    ]
                }
            },
            "highlight": {
                "pre_tags": "<strong style='color:red'>",
                "post_tags": "</strong>",
                "fields": {
                    "subject": {}
                }
            },
            "from": offset,
            "size": limit,
            "_source": {
                "excludes": ["answer"]
            }
        }
        if not subject:
            query['query']['bool']['should'].append({"match_all": {}})
        else:
            query['query']['bool']['should'] = [
                {"match": {
                    "subject": subject
                }}
            ]

        try:
            # print(query)
            es_res = es.search(index='topic', body=query)  # body里面传query

            def highlight(i):
                if i.get('highlight'):
                    i['_source']['subject'] = i['highlight']['subject'][0]
                return i['_source']

            count = es_res["hits"]["total"]["value"]
            subject_url = f'subject={subject}' if subject else ''
            next = f'http://127.0.0.1:8000/work/topics/search/?{subject_url}&offset={offset + limit}&size={limit}' if offset + limit < count else None
            previous = f'http://127.0.0.1:8000/work/topics/search/?{subject_url}&offset={offset - limit}&size={limit}' if offset > 0 else None

            results = [highlight(i) for i in es_res["hits"]["hits"]]
            res = {
                "count": es_res["hits"]["total"]["value"],
                "next": next,
                "previous": previous,
                "results": results
            }
        except elasticsearch.exceptions.NotFoundError:
            res = {
                "count": 0,
                "next": None,
                "previous": None,
                "results": []
            }
        return Response(res)
